home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / drgnsmth.cpt / Dragonsmith 1.1 / Base files / DFileCleaver.c next >
Encoding:
C/C++ Source or Header  |  1992-10-11  |  11.9 KB  |  449 lines

  1. /*
  2.     DFileCleaver.c
  3.     
  4.     A Dragon subspecies developed using THINK C 5.0
  5.  
  6.     Splits text files into smaller segments.
  7.     
  8.     NOTE:    This sub-Dragon is in development.  I wrote it to do a very specific task: split up dragonsmith10b2.hqx
  9.             into files of 24800 bytes for mailing to info-mac@sumex-aim.stanford.edu (my local mail system won't
  10.             send anything > 25000 bytes long).
  11.             
  12.             There's a lot of room for improvement ╤ preferences handling in particular.  You might notice that it's being
  13.             designed to split files in more than one way, but the byte-count method is all that I've implemented so far.
  14.             
  15.             Things that need work are marked PMH.
  16.     
  17.     Copyright ⌐ 1992 by Paul M. Hoffman
  18.     Send comments or suggestions to paul.hoffman@um.cc.umich.edu
  19.     
  20.     There are no restrictions on the use or distribution of applications developed using Dragonsmith.
  21.     This source code may be freely used, altered, and distributed in any way as long as:
  22.         1.    It is GIVEN away rather than sold (unless expressly permitted by the author)
  23.         2.    These statements and the above copyright notice are left intact.
  24.  
  25. */
  26.  
  27. #include    "Dragon.h"
  28. #include    "FileUtils.h"
  29. #include    "MenuUtils.h"
  30. #include    "StringUtils.h"
  31. #include    "HandleUtils.h"
  32. #include    <Types.h>
  33.  
  34. #define    cRETURN    '\r'
  35. #define    cNIL            '\0'
  36. #define    cCOLON        ':'
  37. #define    cELLIPSIS    '╔'        // Yes, that's one char (option-semicolon)
  38.  
  39. enum {
  40.     mOptions = mEdit + 1
  41. };
  42.  
  43. enum {
  44.     iSplitAt = 1,
  45.     iSegmentSize
  46. };
  47.  
  48. // Structure of the DFileCleaver preferences resource ('SSiz' 128)
  49. typedef struct {
  50.     short    splitType;
  51.     short    numSegs;
  52.     long        segSize;
  53.     Boolean    filler;
  54.     Boolean    useAfterText;
  55.     Boolean    useAtText;
  56.     Boolean    useBeforeText;
  57. } SplitStrategy;
  58.  
  59. // Possible values for splitType
  60. enum {
  61.     kSplitByNumSegs,
  62.     kSplitBySegSize,
  63.     kSplitByTextMatch
  64. };
  65.  
  66. enum {
  67.     prefSplitStrategy = 1,        // Split strategy preferences ID
  68.     prefSplitAfterText,            // Text AFTER which a split may be made
  69.     prefSplitAtText,            // Text AT which a split may be made
  70.     prefSplitBeforeText            // Text BEFORE which a split may be made
  71. };
  72.  
  73. #define    rAskSegSize        130        // ID of the "Segment Size╔" dialog
  74. #define    iSegSizeText        4        // Item number of the edit text item containing the segment size
  75.  
  76. class DFileCleaver: public Dragon {
  77.  
  78.     protected:
  79.         Boolean        askForSpecs;
  80.         SplitStrategy    **strategy;
  81.         short        splitType;
  82.         short        numSegments;
  83.         long            segmentSize;
  84.         Handle        buffer;            // Read & write buffer
  85.         long            bufferSize;
  86.  
  87.         MenuHandle    optionsMenu;
  88.         
  89.     public:
  90.                         DFileCleaver (void);
  91.     
  92.     protected:
  93.         virtual void        BeginProcessing (void);        // Override
  94.         virtual void        ReadPrefs (void);            // Override
  95.         virtual void        ProcessFile (void);
  96.  
  97.         virtual void        SetUpMenus (void);
  98.         virtual void        DoMenu (long menuItemCode);
  99.         virtual void        DoOptionsMenu (short itemNum);
  100.         virtual void        AdjustMenusBusy (void);
  101.         virtual void        AdjustMenusIdle (void);
  102.  
  103.         virtual OSErr        SplitFile (FSSpec *srcFile);
  104.         virtual OSErr        ExtractSegFile (FSSpec *srcFile, short srcRefNum, short segNum);
  105.         virtual OSErr        CreateSegFile (FSSpec *fss, unsigned char *sourceName, short segNum);
  106.         virtual void        MakeSegFileName (unsigned char *sourceName, short segNum);
  107.         virtual OSErr        CopyBetweenFiles (short source, short dest);
  108.         virtual OSErr        CopyFileRange (short source, short dest, long rangeSize);
  109.         virtual OSErr        CopyByTextMatch (short source, short dest);
  110.         virtual Boolean    OKToReplace (FSSpec *fss);
  111.         virtual Boolean    SplitSpecsInvalid (void);
  112.         virtual Boolean    AskForSplitSpecs (void);
  113.         virtual Boolean    AskForSegmentSize (long *num);
  114.         virtual Boolean    SplitFailed (FSSpec *sourceFss, OSErr err);
  115. };
  116.  
  117. Dragon *CreateGDragon (void)
  118. {
  119.     return (Dragon *) new DFileCleaver;
  120. }
  121.  
  122. DFileCleaver::DFileCleaver (void)
  123. {
  124.     autoQuit = FALSE;            // This will be overridden by the 'DrPr' prerefence resource
  125.     dirDepthLimit = 0;            // Ditto
  126.  
  127.     askForSpecs = TRUE;        // Should this be set in preferences->Init??
  128.     splitType = kSplitBySegSize;
  129.     numSegments = 0;
  130.     segmentSize = 24800L;        // Last-ditch default
  131.     strategy = NULL;
  132.  
  133.     buffer = NULL;
  134.     bufferSize = 0L;
  135.     
  136.     optionsMenu = NULL;
  137. }
  138.  
  139. void DFileCleaver::ReadPrefs (void)
  140. {
  141.     inherited::ReadPrefs ();
  142.     
  143.     strategy = (SplitStrategy **) preferences->GetPrefResource (prefSplitStrategy);
  144.     if (strategy != NULL) {
  145.         HNoPurge ((Handle) strategy);
  146.         segmentSize = (*strategy)->segSize;        // PMH ╤ we ignore everything else in the struct!!
  147.     }
  148.     askForSpecs = SplitSpecsInvalid ();        // PMH ╤ get askForSpecs from the prefs file?
  149. }
  150.  
  151. void DFileCleaver::SetUpMenus (void)
  152. {
  153.     inherited::SetUpMenus ();            // Add the Apple, File, and Edit menus
  154.  
  155.     optionsMenu = GetMenu (mOptions);
  156.     InsertMenu (optionsMenu, 0);
  157.     
  158.     DrawMenuBar ();
  159. }
  160.  
  161. void DFileCleaver::DoMenu (long menuItemCode)
  162. {
  163.     short    menuID, itemNum;
  164.  
  165.     menuID = menuItemCode >> 16;
  166.     itemNum = menuItemCode & 0xFFFF;
  167.  
  168.     if (menuID == mOptions)
  169.         DoOptionsMenu (itemNum);
  170.     else
  171.         inherited::DoMenu (menuItemCode);
  172. }
  173.  
  174. void DFileCleaver::DoOptionsMenu (short itemNum)
  175. {
  176.     switch (itemNum) {
  177.         case iSplitAt:
  178.             // PMH ╤ we need to put something here (obviously)
  179.             break;
  180.         case iSegmentSize:
  181.             (void) AskForSplitSpecs ();    // PMH ╤ need to rearrange, rename methods
  182.             break;
  183.         default:
  184.             break;
  185.     }
  186. }
  187.  
  188. void DFileCleaver::AdjustMenusBusy (void)
  189. {
  190.     inherited::AdjustMenusBusy ();
  191.     DisableItem (optionsMenu, 0);    // Disable the Options menu
  192. }
  193.  
  194. void DFileCleaver::AdjustMenusIdle (void)
  195. {
  196.     inherited::AdjustMenusIdle ();
  197.     EnableItem (optionsMenu, 0);        // Enable the options menu
  198. }
  199.  
  200. void DFileCleaver::BeginProcessing (void)
  201. {
  202.     Boolean        ok = TRUE;
  203.  
  204.     // Make sure the specifications for the split(s) have been set
  205.     if (askForSpecs || SplitSpecsInvalid ())
  206.         ok = AskForSplitSpecs ();
  207.     if (!ok)
  208.         StopProcessing (userCanceledErr);
  209.         
  210.     inherited::BeginProcessing ();
  211. }
  212.  
  213. void DFileCleaver::ProcessFile (void)
  214. {
  215.     OSErr    err;
  216.     
  217.     if ((err = SplitFile (curDocFSS)) != noErr && SplitFailed (curDocFSS, err))
  218.         StopProcessing (err);
  219. }
  220.  
  221. OSErr DFileCleaver::SplitFile (FSSpec *srcFile)
  222. {
  223.     short    srcRefNum, destRefNum;
  224.     OSErr    err;
  225.     short    segNum;
  226.     long        actualSize;
  227.     Boolean    ok;
  228.  
  229.     // If the file is smaller than the segment size, there's no need to split it
  230.     if (PBDataForkSize (curDocPB) <= segmentSize)
  231.         return noErr;        // PMH ╤ or should we return an error?
  232.             
  233.     err = FSpOpenDF (srcFile, fsRdPerm, &srcRefNum);
  234.     if (err == noErr) {
  235.         bufferSize = segmentSize;
  236.         if (!ok) return -1;        // PMH ╤ impromptu error code
  237.         
  238.         splitType = 0;            // PMH ╤ hard-coded garbage
  239.         if (!ok) return -1;        // PMH ╤ impromptu error code
  240.         actualSize = bufferSize;
  241.         buffer = BigHandle (&actualSize);                // Returns the actual block size in actualSize
  242.         bufferSize = actualSize;
  243.         segNum = 1;
  244.         do
  245.             err = ExtractSegFile (srcFile, srcRefNum, segNum++);
  246.         while (err == noErr);
  247.         FSClose (srcRefNum);
  248.         DisposHandle (buffer);
  249.     }
  250.     return (err == eofErr) ? noErr : err;
  251. }
  252.  
  253. OSErr DFileCleaver::ExtractSegFile (FSSpec *srcFile, short srcRefNum, short segNum)
  254. {
  255.     FSSpec    destFile;
  256.     short    destRefNum;
  257.     OSErr    err, copyErr;
  258.     
  259.     DoBusy ();                            // Give other processes a little time between segments
  260.     
  261.     destFile.vRefNum = srcFile->vRefNum;        // PMH ╤ leave segments in the same place
  262.     destFile.parID = srcFile->parID;            //    as the file we're splitting
  263.     
  264.     // Make a file for the next segment, basing its name on that of the original
  265.     err = CreateSegFile (&destFile, (unsigned char *) &srcFile->name, segNum);
  266.     if (err == noErr) {
  267.         err = FSpOpenDF (&destFile, fsWrPerm, &destRefNum);
  268.         if (err == noErr) {
  269.             err = copyErr = CopyBetweenFiles (srcRefNum, destRefNum);
  270.             if (copyErr == noErr || copyErr == eofErr) {
  271.                 err = FSClose (destRefNum);
  272.                 (void) FlushVol (NULL, destFile.vRefNum);        // Ignore any errors ╤ what would we do??
  273.             }
  274.         }
  275.         if (err != noErr)
  276.             (void) FSpDelete (&destRefNum);
  277.     }
  278.     return (err == noErr) ? copyErr : err;
  279. }
  280.  
  281. OSErr DFileCleaver::CreateSegFile (FSSpec *fss, unsigned char *sourceName, short segNum)
  282. {
  283.     OSErr    err;
  284.     Boolean    ok;
  285.     
  286.     CopyPStr (sourceName, fss->name);
  287.     MakeSegFileName ((unsigned char *) &fss->name, segNum);
  288.     err = FSpCreate (fss, curFileCreator, curFileType, smSystemScript);
  289.     if (err == dupFNErr) {
  290.         ok = OKToReplace (fss);
  291.         if (ok) {
  292.             err = FSpDelete (fss);
  293.             if (err == noErr)
  294.                 err = FSpCreate (fss, curFileCreator, curFileType, smSystemScript);
  295.         }
  296.     }
  297.     return err;
  298. }
  299.  
  300. void DFileCleaver::MakeSegFileName (unsigned char *sourceName, short segNum)
  301. {
  302.     #define    maxShortString    10        // "\p32767" => 5 digits would be the longest
  303.                                     //    in USA, but other scripts might produce
  304.                                     //    a bigger string from a short, so we leave
  305.                                     //    a little extra room
  306.     #define    maxFileName        63        // Make allowances for MFS (yeah, yeah, OK)
  307.     
  308.     unsigned char        segNumStr [maxShortString];
  309.     unsigned short    len;
  310.     unsigned char        partString [] = "\p-pt";    // PMH ╤ hard-coded string
  311.     unsigned char        chopLen;
  312.     
  313.     chopLen = maxFileName - partString [0] - maxShortString;
  314.     if (sourceName [0] > chopLen) {
  315.         sourceName [0] = chopLen;
  316.         sourceName [chopLen] = cELLIPSIS;
  317.     }
  318.     AppendPStr (sourceName, partString);
  319.     NumToString (segNum, segNumStr);
  320.     AppendPStr (sourceName, segNumStr);
  321. }
  322.  
  323. OSErr DFileCleaver::CopyBetweenFiles (short source, short dest)
  324. {
  325.     Boolean    ok;
  326.     
  327.     if (splitType == -1)                                // PMH ╤ hard-coded garbage
  328.         return CopyByTextMatch (source, dest);        // PMH ╤ change this?
  329.     else
  330.         return CopyFileRange (source, dest, segmentSize);
  331. }
  332.  
  333. OSErr DFileCleaver::CopyFileRange (short source, short dest, long rangeSize)
  334. {
  335.     OSErr    err = noErr, copyErr = noErr;
  336.     long        copySize, bytesToCopy;
  337.     
  338.     copySize = bufferSize;
  339.     for (bytesToCopy = rangeSize; bytesToCopy > 0L && err == noErr && copyErr != eofErr; bytesToCopy -= copySize) {
  340.         if (copySize > bytesToCopy)
  341.             copySize = bytesToCopy;
  342.         err = copyErr = FSRead (source, ©Size, *buffer);
  343.         if (err == noErr || err == eofErr)
  344.             err = FSWrite (dest, ©Size, *buffer);
  345.     }
  346.     return (err == noErr) ? copyErr : err;
  347. }
  348.  
  349. OSErr DFileCleaver::CopyByTextMatch (short source, short dest)
  350. {
  351.     // Not implemented yet
  352. }
  353.  
  354. Boolean DFileCleaver::OKToReplace (FSSpec *fss)
  355. {
  356.     return TRUE;    // PMH ╤ always replace
  357. }
  358.  
  359. Boolean DFileCleaver::AskForSplitSpecs (void)
  360. {
  361.     OSErr    err;
  362.     Boolean    ok;
  363.     long        sizeReplied = segmentSize;
  364.     
  365.     ok = AskForSegmentSize (&sizeReplied);
  366.     if (ok && sizeReplied != 0L && sizeReplied != segmentSize) {    // Did the user change it (to a non-zero value)?
  367.         if (strategy!= NULL) {
  368.             (*strategy)->segSize = segmentSize = sizeReplied;
  369.             preferences->SavePrefResource (prefSplitStrategy);
  370.         }
  371.         askForSpecs = FALSE;                                // Don't need to ask again
  372.     }
  373.     return ok;
  374. }
  375.  
  376. Boolean DFileCleaver::AskForSegmentSize (long *num)
  377. {
  378.     DialogPtr        dialog;
  379.     short        itemHit, itemType;
  380.     Handle        itemHndl;
  381.     Rect            itemRect;
  382.     Str255        numString;
  383.     Boolean        done;
  384.     
  385.     if (num == NULL || *num < 0)
  386.         return FALSE;
  387.     
  388.     if (!InteractWithUser (60*60)) {
  389.         StopProcessing (userCanceledErr);
  390.         return FALSE;
  391.     }
  392.     
  393.     dialog = GetNewDialog (rAskSegSize, NULL, (WindowPtr) -1);
  394.     if (dialog == NULL)
  395.         return FALSE;
  396.         
  397.     NumToString (*num, numString);
  398.     
  399.     GetDItem (dialog, iSegSizeText, &itemType, &itemHndl, &itemRect);
  400.     SetIText (itemHndl, numString);
  401.     SelIText (dialog, iSegSizeText, 0, 32767);
  402.     
  403.     ShowWindow ((WindowPtr) dialog);
  404.  
  405.     do {
  406.         do
  407.             ModalDialog (NULL, &itemHit);
  408.         while (itemHit != ok && itemHit != cancel);
  409.         
  410.         done = TRUE;            // User clicked on Cancel or OK (or pressed <Return> or <Enter>)
  411.         if (itemHit == ok) {
  412.             GetIText (itemHndl, numString);
  413.             done = PStrToULong (numString, num);        // PStrToULong returns FALSE if it's not a valid unsigned long
  414.         }
  415.     } while (!done);
  416.     
  417.     HideWindow ((WindowPtr) dialog);
  418.     DisposDialog (dialog);
  419.     
  420.     return (itemHit != cancel);
  421. }
  422.  
  423. Boolean DFileCleaver::SplitSpecsInvalid (void)
  424. {
  425.     switch (splitType) {
  426.         case kSplitByNumSegs:
  427.             return TRUE;
  428.             break;
  429.         case kSplitBySegSize:            // PMH ╤ we only do kSplitBySegSize
  430.             if (segmentSize < 1)
  431.                 return TRUE;
  432.             break;
  433.         case kSplitByTextMatch:
  434.         default:
  435.             return TRUE;
  436.     }
  437.     
  438.     // If we reach this point, then the specs are not invalid
  439.     return FALSE;
  440. }
  441.  
  442. Boolean DFileCleaver::SplitFailed (FSSpec *sourceFss, OSErr err)
  443. {
  444.     // Display an alert telling the user that there was a problem and asking if they want to continue.  Return TRUE if
  445.     //    they chose "Stop" or FALSE if they want to continue splitting the remaining files
  446.     
  447.     return FALSE;        // PMH ╤ I'd really like to put something more sophisticated here╔
  448. }
  449.